/**
 * \file  combinatory.h
 * \brief Definition of CombinatoryNewton template.
 *
 * ASO: Antiviral Sequence Optimizer
 * <http://aso.googlecode.com/>
 * Copyright (C) 2010 Velez Ezequiel, Peralta Andres, FuDePAN
 *
 * This file is part of the ASO project.
 *
 * Contents:       Header file for ASO providing template CombinatoryNewton.
 *
 * System:         ASO
 * Language:       C++
 *
 * Author:         Velez Ezequiel, Peralta Andres
 * E-Mail:
 *
 * ASO is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ASO is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ASO.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef COMBINATORY_H
#define COMBINATORY_H

#include <iterator>
#include <mili/mili.h>
#include "aso-core/observer.h"

//! T es el repositorio al cual se le calcula la combinatoria
template <class T>
class CombinatoryNewton: public CombinatoryPolicy
{
public:
    typedef typename T::value_type TValue;
    typedef std::vector<TValue> Combination;

protected:
    typedef typename T::const_iterator TCIterator;

    const size_t MIN;
    const size_t MAX;
    Observer<T, bool>* observed_by;     //! contiene el observador

    inline bool combinations_builder(Combination& combination, size_t pos, TCIterator start, TCIterator end);
    inline bool choose(const T& container, size_t n);
    inline bool notify(const Combination& comb);

public:
    //! Constructor
    CombinatoryNewton(size_t min, size_t max, const std::string& name): CombinatoryPolicy(name), MIN(min), MAX(max), observed_by()
    {}

    inline bool combine(const T& elements, Observer<T, bool>* observer, CombinationStatus& status);
};
//----------------------------------------------------------------------------------

template<class T>
inline bool CombinatoryNewton<T>::notify(const Combination& comb)
{
    T aux;
    mili::CAutonomousIterator<Combination> combit(comb);
    while (!combit.end())
    {
        mili::insert_into(aux, *combit);
        ++combit;
    }

    return observed_by->update(&aux);
}

template<class T>
inline bool CombinatoryNewton<T>::combinations_builder(Combination& combination, size_t pos, TCIterator start, TCIterator end)
{
    TCIterator it = start;
    bool continueComb(true);
    if (pos == 0)
        continueComb = notify(combination);
    else
    {
        while (it != end && continueComb)
        {
            combination[pos - 1] = *it;
            continueComb = combinations_builder(combination, pos - 1, ++it, end);
        }
    }
    return  continueComb;
}

template<class T>
inline bool CombinatoryNewton<T>::choose(const T& container, size_t n)
{
    Combination combination(n);
    return combinations_builder(combination, n, container.begin(), container.end());
}

/*! realiza el calculo combinatorio a lo largo de un rango */
template<class T>
inline bool CombinatoryNewton<T>::combine(const T& elements, Observer<T, bool>* observer, CombinationStatus& status)
{
    const size_t size = elements.size();
    status.status = Success;
    status.name = get_name();

    bool continueComb(true);
    observed_by = observer;

    if (size < MIN)
        status.status = Error;
    else
    {
        size_t k = MIN;
        const size_t k_max = std::min(size, MAX);
        while (k <= k_max && continueComb)
        {
            continueComb = choose(elements , k);
            ++k;
        }
    }
    return continueComb;
}

#endif
